home *** CD-ROM | disk | FTP | other *** search
/ PC go! 2018 July / PCgo 07-2018 CD-ROM Germany.iso / nw.pak / Unnamed File 000141.txt < prev    next >
Encoding:
Text File  |  2015-07-29  |  10.6 KB  |  353 lines

  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5. // Copyright 2014 The Chromium Authors. All rights reserved.
  6. // Use of this source code is governed by a BSD-style license that can be
  7. // found in the LICENSE file.
  8.  
  9. cr.exportPath('cr.ui');
  10.  
  11. /**
  12.  * Reverse the child elements of any found button strips if they haven't already
  13.  * been reversed. This is necessary because WebKit does not alter the tab order
  14.  * for elements that are visually reversed using flex-direction: reverse, and
  15.  * the button order is reversed for views. See http://webk.it/62664 for more
  16.  * information.
  17.  * @param {Node=} opt_root Starting point for button strips to reverse.
  18.  */
  19. cr.ui.reverseButtonStrips = function(opt_root) {
  20.   if (!(cr.isWindows || cr.isChromeOS)) {
  21.     // Only reverse on platforms that need it (differ from the HTML order).
  22.     return;
  23.   }
  24.  
  25.   var root = opt_root || document;
  26.   var buttonStrips = root.querySelectorAll('.button-strip:not([reversed])');
  27.   for (var j = 0; j < buttonStrips.length; j++) {
  28.     var buttonStrip = buttonStrips[j];
  29.  
  30.     var childNodes = buttonStrip.childNodes;
  31.     for (var i = childNodes.length - 1; i >= 0; i--) {
  32.       buttonStrip.appendChild(childNodes[i]);
  33.     }
  34.  
  35.     buttonStrip.setAttribute('reversed', '');
  36.   }
  37. };
  38.  
  39.  
  40. cr.define('cr.ui.pageManager', function() {
  41.   var PageManager = cr.ui.pageManager.PageManager;
  42.  
  43.   /**
  44.    * Base class for pages that can be shown and hidden by PageManager. Each Page
  45.    * is like a node in a forest, corresponding to a particular div. At any
  46.    * point, one root Page is visible, and any visible Page can show a child Page
  47.    * as an overlay. The host of the root Page(s) should provide a container div
  48.    * for each nested level to enforce the stack order of overlays.
  49.    * @constructor
  50.    * @param {string} name Page name.
  51.    * @param {string} title Page title, used for history.
  52.    * @param {string} pageDivName ID of the div corresponding to the page.
  53.    * @extends {cr.EventTarget}
  54.    */
  55.   function Page(name, title, pageDivName) {
  56.     this.name = name;
  57.     this.title = title;
  58.     this.pageDivName = pageDivName;
  59.     this.pageDiv = $(this.pageDivName);
  60.     // |pageDiv.page| is set to the page object (this) when the page is visible
  61.     // to track which page is being shown when multiple pages can share the same
  62.     // underlying div.
  63.     this.pageDiv.page = null;
  64.     this.tab = null;
  65.     this.lastFocusedElement = null;
  66.     this.hash = '';
  67.   }
  68.  
  69.   Page.prototype = {
  70.     __proto__: cr.EventTarget.prototype,
  71.  
  72.     /**
  73.      * The parent page of this page, or null for root pages.
  74.      * @type {cr.ui.pageManager.Page}
  75.      */
  76.     parentPage: null,
  77.  
  78.     /**
  79.      * The section on the parent page that is associated with this page.
  80.      * Can be null.
  81.      * @type {Element}
  82.      */
  83.     associatedSection: null,
  84.  
  85.     /**
  86.      * An array of controls that are associated with this page. The first
  87.      * control should be located on a root page.
  88.      * @type {Array.<Element>}
  89.      */
  90.     associatedControls: null,
  91.  
  92.     /**
  93.      * If true, this page should always be considered the top-most page when
  94.      * visible.
  95.      * @type {boolean}
  96.      */
  97.     alwaysOnTop_: false,
  98.  
  99.     /**
  100.      * Initializes page content.
  101.      */
  102.     initializePage: function() {},
  103.  
  104.     /**
  105.      * Called by the PageManager when this.hash changes while the page is
  106.      * already visible. This is analogous to the hashchange DOM event.
  107.      */
  108.     didChangeHash: function() {},
  109.  
  110.     /**
  111.      * Sets focus on the first focusable element. Override for a custom focus
  112.      * strategy.
  113.      */
  114.     focus: function() {
  115.       // Do not change focus if any control on this page is already focused.
  116.       if (this.pageDiv.contains(document.activeElement))
  117.         return;
  118.  
  119.       var elements = this.pageDiv.querySelectorAll(
  120.           'input, list, select, textarea, button');
  121.       for (var i = 0; i < elements.length; i++) {
  122.         var element = elements[i];
  123.         // Try to focus. If fails, then continue.
  124.         element.focus();
  125.         if (document.activeElement == element)
  126.           return;
  127.       }
  128.     },
  129.  
  130.     /**
  131.      * Reverse any buttons strips in this page (only applies to overlays).
  132.      * @see cr.ui.reverseButtonStrips for an explanation of why this is
  133.      * necessary and when it's done.
  134.      */
  135.     reverseButtonStrip: function() {
  136.       assert(this.isOverlay);
  137.       cr.ui.reverseButtonStrips(this.pageDiv);
  138.     },
  139.  
  140.     /**
  141.      * Whether it should be possible to show the page.
  142.      * @return {boolean} True if the page should be shown.
  143.      */
  144.     canShowPage: function() {
  145.       return true;
  146.     },
  147.  
  148.     /**
  149.      * Updates the hash of the current page. If the page is topmost, the history
  150.      * state is updated.
  151.      * @param {string} hash The new hash value. Like location.hash, this
  152.      *     should include the leading '#' if not empty.
  153.      */
  154.     setHash: function(hash) {
  155.       if (this.hash == hash)
  156.         return;
  157.       this.hash = hash;
  158.       PageManager.onPageHashChanged(this);
  159.     },
  160.  
  161.     /**
  162.      * Called after the page has been shown.
  163.      */
  164.     didShowPage: function() {},
  165.  
  166.     /**
  167.      * Set this to handle cancelling an overlay (and skip some typical steps).
  168.      * @see {cr.ui.PageManager.prototype.cancelOverlay}
  169.      * @type {?Function}
  170.      */
  171.     handleCancel: null,
  172.  
  173.     /**
  174.      * Called before the page will be hidden, e.g., when a different root page
  175.      * will be shown.
  176.      */
  177.     willHidePage: function() {},
  178.  
  179.     /**
  180.      * Called after the overlay has been closed.
  181.      */
  182.     didClosePage: function() {},
  183.  
  184.     /**
  185.      * Gets the container div for this page if it is an overlay.
  186.      * @type {HTMLDivElement}
  187.      */
  188.     get container() {
  189.       assert(this.isOverlay);
  190.       return this.pageDiv.parentNode;
  191.     },
  192.  
  193.     /**
  194.      * Gets page visibility state.
  195.      * @type {boolean}
  196.      */
  197.     get visible() {
  198.       // If this is an overlay dialog it is no longer considered visible while
  199.       // the overlay is fading out. See http://crbug.com/118629.
  200.       if (this.isOverlay &&
  201.           this.container.classList.contains('transparent')) {
  202.         return false;
  203.       }
  204.       if (this.pageDiv.hidden)
  205.         return false;
  206.       return this.pageDiv.page == this;
  207.     },
  208.  
  209.     /**
  210.      * Sets page visibility.
  211.      * @type {boolean}
  212.      */
  213.     set visible(visible) {
  214.       if ((this.visible && visible) || (!this.visible && !visible))
  215.         return;
  216.  
  217.       // If using an overlay, the visibility of the dialog is toggled at the
  218.       // same time as the overlay to show the dialog's out transition. This
  219.       // is handled in setOverlayVisible.
  220.       if (this.isOverlay) {
  221.         this.setOverlayVisible_(visible);
  222.       } else {
  223.         this.pageDiv.page = this;
  224.         this.pageDiv.hidden = !visible;
  225.         PageManager.onPageVisibilityChanged(this);
  226.       }
  227.  
  228.       cr.dispatchPropertyChange(this, 'visible', visible, !visible);
  229.     },
  230.  
  231.     /**
  232.      * Whether the page is considered 'sticky', such that it will remain a root
  233.      * page even if sub-pages change.
  234.      * @type {boolean} True if this page is sticky.
  235.      */
  236.     get sticky() {
  237.       return false;
  238.     },
  239.  
  240.     /**
  241.      * @type {boolean} True if this page should always be considered the
  242.      *     top-most page when visible.
  243.      */
  244.     get alwaysOnTop() {
  245.       return this.alwaysOnTop_;
  246.     },
  247.  
  248.     /**
  249.      * @type {boolean} True if this page should always be considered the
  250.      *     top-most page when visible. Only overlays can be always on top.
  251.      */
  252.     set alwaysOnTop(value) {
  253.       assert(this.isOverlay);
  254.       this.alwaysOnTop_ = value;
  255.     },
  256.  
  257.     /**
  258.      * Shows or hides an overlay (including any visible dialog).
  259.      * @param {boolean} visible Whether the overlay should be visible or not.
  260.      * @private
  261.      */
  262.     setOverlayVisible_: function(visible) {
  263.       assert(this.isOverlay);
  264.       var pageDiv = this.pageDiv;
  265.       var container = this.container;
  266.  
  267.       if (container.hidden != visible) {
  268.         if (visible) {
  269.           // If the container is set hidden and then immediately set visible
  270.           // again, the fadeCompleted_ callback would cause it to be erroneously
  271.           // hidden again. Removing the transparent tag avoids that.
  272.           container.classList.remove('transparent');
  273.  
  274.           // Hide all dialogs in this container since a different one may have
  275.           // been previously visible before fading out.
  276.           var pages = container.querySelectorAll('.page');
  277.           for (var i = 0; i < pages.length; i++)
  278.             pages[i].hidden = true;
  279.           // Show the new dialog.
  280.           pageDiv.hidden = false;
  281.           pageDiv.page = this;
  282.         }
  283.         return;
  284.       }
  285.  
  286.       var self = this;
  287.       var loading = PageManager.isLoading();
  288.       if (!loading) {
  289.         // TODO(flackr): Use an event delegate to avoid having to subscribe and
  290.         // unsubscribe for webkitTransitionEnd events.
  291.         container.addEventListener('webkitTransitionEnd', function f(e) {
  292.             var propName = e.propertyName;
  293.             if (e.target != e.currentTarget ||
  294.                 (propName && propName != 'opacity')) {
  295.               return;
  296.             }
  297.             container.removeEventListener('webkitTransitionEnd', f);
  298.             self.fadeCompleted_();
  299.         });
  300.         // -webkit-transition is 200ms. Let's wait for 400ms.
  301.         ensureTransitionEndEvent(container, 400);
  302.       }
  303.  
  304.       if (visible) {
  305.         container.hidden = false;
  306.         pageDiv.hidden = false;
  307.         pageDiv.page = this;
  308.         // NOTE: This is a hacky way to force the container to layout which
  309.         // will allow us to trigger the webkit transition.
  310.         /** @suppress {uselessCode} */
  311.         container.scrollTop;
  312.  
  313.         this.pageDiv.removeAttribute('aria-hidden');
  314.         if (this.parentPage) {
  315.           this.parentPage.pageDiv.parentElement.setAttribute('aria-hidden',
  316.                                                              true);
  317.         }
  318.         container.classList.remove('transparent');
  319.         PageManager.onPageVisibilityChanged(this);
  320.       } else {
  321.         // Kick change events for text fields.
  322.         if (pageDiv.contains(document.activeElement))
  323.           document.activeElement.blur();
  324.         container.classList.add('transparent');
  325.       }
  326.  
  327.       if (loading)
  328.         this.fadeCompleted_();
  329.     },
  330.  
  331.     /**
  332.      * Called when a container opacity transition finishes.
  333.      * @private
  334.      */
  335.     fadeCompleted_: function() {
  336.       if (this.container.classList.contains('transparent')) {
  337.         this.pageDiv.hidden = true;
  338.         this.container.hidden = true;
  339.  
  340.         if (this.parentPage)
  341.           this.parentPage.pageDiv.parentElement.removeAttribute('aria-hidden');
  342.  
  343.         PageManager.onPageVisibilityChanged(this);
  344.       }
  345.     },
  346.   };
  347.  
  348.   // Export
  349.   return {
  350.     Page: Page
  351.   };
  352. });
  353.